package de.akabahn.ebd.stw.zdxfc.prs.fst;

import static de.akabahn.ebd.comm.Util.waitForLatch;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.AUFLOESUNG_OK;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BIT0;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BIT1;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.FAHRSTRASSE_NOK;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.FAHRSTRASSE_OK;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.FAHRSTRASSE_SPEICHERN;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.WARTEZEIT_WEICHENLAUF;

import java.util.concurrent.CountDownLatch;

import org.apache.log4j.Logger;

import de.akabahn.ebd.comm.dbd.DBDParameter;
import de.akabahn.ebd.dbd.SwitchPosition;
import de.akabahn.ebd.stw.zdxfc.prs.ZDXFCMain;
import de.akabahn.ebd.stw.zdxfc.prs.elemente.FahrwegElement;
import de.akabahn.ebd.stw.zdxfc.prs.elemente.Gleis;
import de.akabahn.ebd.stw.zdxfc.prs.elemente.Signal;
import de.akabahn.ebd.stw.zdxfc.prs.elemente.Weiche;

/**
 * Basisklasse des Fahrwegelements <code>Fahrstrasse</code>. Enthaelt die Eigenschaften, die fuer alle Fahrstrassen
 * gleich sind.
 * <br>
 * Bitcodierung der Melder der Fahrstrassentasten<br>
 * <pre>
 * BIT1 BIT0  BEDEUTUNG
 *    0    0  AUS
 *    0    1  AN
 *    1    0  BLINKT
 * </pre>
 * 
 * @author Peter Eimann
 * @since 1.0
 * @version 1.2
 * @see FstWeiche
 * @see FstGleis
 * @see FstSignal
 */
public abstract class FahrStrasse extends FahrwegElement {

	private final static Logger LOG = Logger.getLogger(FahrStrasse.class);
	
	private boolean gespeichert;
	private boolean verschlossen;
	private boolean einstellenVorbereiten;
	private long aufloeseZeit;
	private String tastenMelderId;
	private String[] startSignale;

	protected FstWeiche[] fstWeichen;
	protected FstGleis[] fstGleise;
	protected FstSignal[] fstSignale;

	/**
	 * Initialisierung der Klasse
	 * 
	 * @param id
	 *            Eindeutige ID des Elements, wird fuer die Referenz auf das
	 *            Element benoetigt (die ID ist der Schluessel in den Maps, in
	 *            denen das Element gespeichert ist.
	 * @param tastenMelderId
	 *            Name der DBD-Variablen fuer den der Fahrstrasse zugeordneten
	 *            Tastenmelder im Bedienpult
	 * @param main
	 *            Referenz auf die Hauptklasse
	 * @param startSignale
	 *            Die Startsignale der Fahrstrasse
	 */
	public FahrStrasse(String id, String tastenMelderId, ZDXFCMain main, String...startSignale) {
		super(id, main);
		this.tastenMelderId = tastenMelderId;
		this.startSignale = startSignale;
	}

	/**
	 * Gibt zurueck, ob die Fahrstrasse eingespeichert ist oder nicht
	 * 
	 * @return <code>true</code> - Fahrstrasse ist gespeichert
	 */
	public boolean isGespeichert() {
		return gespeichert;
	}

	/**
	 * Setzt oder loescht die Fahrstrassenspeicherung.<br>
	 * Wird die Fahrstrasse gespeichert, wird der zugehoerige Tastenmelder blinkend ausgeleuchtet.<br>
	 * Wird die Speicherung geloescht (und die Fahrstrasse ist nicht in der Einstellung), wird die Ausleuchtung des 
	 * Tastenmelders geloescht.
	 * 
	 * @param gespeichert Das Speicher-Flag
	 */
	public void setGespeichert(boolean gespeichert) {
		this.gespeichert = gespeichert;
		
		if (gespeichert) {
			main.getDbd().set(tastenMelderId, BIT1);
		} else if (!einstellenVorbereiten) {
			main.getDbd().set(tastenMelderId, 0);
		}
		
		LOG.info(id + ": Gespeichert = " + gespeichert);
	}

	/**
	 * Gibt zurueck, ob die Fahrstrasse verschlossen ist oder nicht
	 * 
	 * @return <code>true</code> - Fahrstrasse ist verschlossen
	 */
	public boolean isVerschlossen() {
		return verschlossen;
	}

	/**
	 * Setzt oder loescht den Verschluss der Fahrstrasse.<br>
	 * Wird die Fahrstrasse verschlossen, wird der zugehoerige Tastenmelder mit Dauerlicht ausgeleuchtet.
	 * 
	 * @param verschlossen Das Verschlossen-Flag
	 */
	public void setVerschlossen(boolean verschlossen) {
		this.verschlossen = verschlossen;
		
		if (verschlossen) {
			main.getDbd().set(tastenMelderId, BIT0);
		}
		
		LOG.info(id + ": Verschlossen = " + verschlossen);
	}

	/**
	 * Gibt an, ob das Einstellen-Vorbereiten Flag gesetzt ist oder nicht
	 * 
	 * @return <code>true</code> - Das Einstellen-Vorbereiten Flag ist gesetzt
	 */
	public boolean isEinstellenVorbereiten() {
		return einstellenVorbereiten;
	}

	/**
	 * Setzt das Einstellen-Vorbereiten Flag
	 * @param einstellenVorbereiten Das Einstellen-Vorbereiten Flag
	 */
	public void setEinstellenVorbereiten(boolean einstellenVorbereiten) {
		this.einstellenVorbereiten = einstellenVorbereiten;
		LOG.info(id + ": Einstellen vorbereiten = " + einstellenVorbereiten);
	}

	/**
	 * Gibt die Aufloesezeit der Fahrstrasse zurueck (Zeitpunkt, zu dem die Fahrstrasse fruehestens aufgeloest werden
	 * kann)
	 * 
	 * @return Die Aufloesezeit (0, wenn Fahrstrasse nicht augeloest werden soll, -1 wenn die Fahrstrasse
	 *         aufgeloest werden kann)
	 */
	public long getAufloeseZeit() {
		return aufloeseZeit;
	}

	/**
	 * Setzt die Aufloesezeit der Fahrstrasse
	 * 
	 * @param aufloeseZeit Die Aufloesezeit
	 */
	public void setAufloeseZeit(long aufloeseZeit) {
		this.aufloeseZeit = aufloeseZeit;
		LOG.info(id + ": Aufloesezeit = " + aufloeseZeit);
	}

	/**
	 * Gibt den Namen der DBD-Variablen des Tastenmelders der Fahrstrassentaste im Bedienpult zurueck
	 * 
	 * @return Den Namen der DBD-Variablen
	 */
	public String getTastenMelderId() {
		return tastenMelderId;
	}

	/**
	 * Prueft, ob die Fahrstrasse eingestellt werden kann.<br>
	 * Ist die Fahrstrasse verschlossen, wird <code>FAHRSTRASSE_NOK</code> zurueckgegeben, anderenfalls wird
	 * <code>pruefenProcess()</code> aufgerufen.
	 * 
	 * @return Das Ergebnis der Fahrstrassenpruefung, <code>FAHRSTRASSE_OK</code>, <code>FAHRSTRASSE_SPEICHERN</code>
	 *         oder <code>FAHRSTRASSE_NOK</code>
	 * @see #pruefenProcess()
	 */
	public int pruefen() {
		int result = isVerschlossen() ? FAHRSTRASSE_NOK : pruefenProcess();
		LOG.info("Pruefen Fahrstrasse " + id + " = " + result);
		return result;
	}

	/**
	 * Stellt die Fahrstrasse ein.<br>
	 * Setzt das Einstellen-Vorbereiten Flag, um zu kennzeichnen, dass sich die Fahrstrasse im Einstellprozess befindet,
	 * leuchtet den Tastenmelder mit Blinklicht aus und stoesst die Weichenumstellung an.<br>
	 * Sind alle Weichen umgelaufen, wird nochmal die Fahrstrassenpruefung aufgerufen, um sicherzustellen, dass alle
	 * Elemente immer noch in Grundstellung stehen. Ist das der Fall, werden die Fahrstrassenweichen und -gleise aktiv
	 * gesetzt, das Einspeicher-Flag geloescht und <code>einstellenProcess()</code> aufgerufen, um die
	 * fahrstrassenspezifischen Einstellungen durchzufuehren.<br>
	 * Ist mindestens eine Weiche nicht in Endlage gelaufen oder war die erneute Fahrstrassenpruefung nicht erfolgreich,
	 * wird die Fahrstrasse eingespeichert.<br>
	 * Als letzte Aktion wird das Einstellen-Vorbereiten Flag wieder geloescht.
	 *
	 * @see #weichenUmstellen()
	 * @see #pruefen()
	 * @see #setzeWeichenAktiv()
	 * @see #setzeGleiseAktiv()
	 * @see #einstellenProcess()
	 */
	public void einstellen() {
		LOG.info("Fahrstrasse " + id + " einstellen");
		
		try {
			setEinstellenVorbereiten(true);
			main.getDbd().set(tastenMelderId, BIT1);

			CountDownLatch latch = weichenUmstellen();

			if (waitForLatch(latch, WARTEZEIT_WEICHENLAUF) && (pruefen() == FAHRSTRASSE_OK)) {
				setzeWeichenAktiv();
				setzeGleiseAktiv();
				setAktiv(true);
				setGespeichert(false);

				einstellenProcess();
			} else {
				setGespeichert(true);
			}
		} finally {
			setEinstellenVorbereiten(false);
		}
	}
	
	/**
	 * Loest die Fahrstrasse auf, wenn die Aufloesezeit gleich <code>AUFLOESUNG_OK</code> ist.<br>
	 * Zur Aufloesung wird die Aufloesezeit auf 0 zurueckgesetzt, das Aktiv-Flag geloescht, die Ausleuchtung des
	 * Tastenmelders geloescht, die Startsignale auf Halt gestellt, die Fahrstrassenweichen und -gleise auf inaktiv
	 * gesetzt und <code>aufloesenProcess()</code> aufgerufen, um die fahrstrassenspezifischen Aufloesungen
	 * durchzufuehren.
	 * 
	 * @see #setzeWeichenAktiv()
	 * @see #setzeGleiseInaktiv()
	 * @see #aufloesenProcess()
	 * @see #hilfsaufloesen()
	 */
	public void aufloesen() {
		LOG.info("Fahrstrasse " + id + " aufloesen");

		if (getAufloeseZeit() == AUFLOESUNG_OK) {
			setAufloeseZeit(0);
			aufloesenProcess();
			haltfallStartsignale();
			setAktiv(false);
			setzeWeichenInaktiv();
			setzeGleiseInaktiv();
			main.getDbd().set(tastenMelderId, 0);
		} else {
			LOG.debug("Aufloesezeit fuer Fahrstrasse " + id + " = " + getAufloeseZeit());
		}
	}
	
	/**
	 * Loest die Fahrstrasse hilfsweise auf (nicht vom Pult aus bedienbar, nur durch direktes Setzen der DBD-Variablen 
	 * mit dem Wert 1 auszuloesen).<br>
	 * Dazu wird der Verschluss der Fahrstrasse aufgeloest, die Aufloesezeit auf <code>AUFLOESUNG_OK</code> gesetzt
	 * und <code>aufloesen()</code> aufgerufen.
	 * @see #aufloesen()
	 */
	public void hilfsaufloesen() {
		LOG.warn("Fahrstrasse " + id + " hilfsaufloesen");

		setVerschlossen(false);
		setAufloeseZeit(AUFLOESUNG_OK);
		aufloesen();
	}
	
	/**
	 * Wird bei Aenderung einer abonnierten DBD-Variablen vom <code>FahrwegElement</code> aufgerufen, wenn die 
	 * Aenderung fuer die Fahrstrasse relevant ist.<br>
	 * Ruft auf:
	 * <ul>
	 * <li><code>onChangeBesetzung(dbd)</code>, wenn der Variablenname mit <code>B</code>
	 * (fuer die Besetztmeldungen) endet</li>
	 * <li><code>onChangeWeichenLage(dbd)</code>, wenn der Variablenname mit <code>I</code>
	 * (fuer die Weichenlage) endet</li>
	 * </ul>
	 * Die Methoden muessen in den konkreten Fahrstrassen implementiert werden, um die Aenderung zu verarbeiten.
	 * 
	 * @param dbd Die geaenderte Variable
	 * @see #onChangeBesetzung(DBDParameter)
	 * @see #onChangeWeichenLage(DBDParameter)
	 */
	@Override
	public void onChangeProcess(DBDParameter dbd) {
		if (dbd.getName().endsWith("B")) {
			onChangeBesetzung(dbd);
		} else if (dbd.getName().endsWith("I")) {
			onChangeWeichenLage(dbd);
		}
	}

	/**
	 * Muss in der konkreten Fahrstrasse implementiert werden, um zu pruefen, ob die Aufloesung einer Fahrstrasse
	 * ueberhaupt vorgenommen werden kann.
	 * 
	 * @return <code>true</code> - Aufloesung der Fahrstrasse kann vorgenommen werden
	 */
	public abstract boolean aufloeseBedinungenOk();

	/**
	 * Muss in der konkreten Fahrstrasse implementiert werden, um die fahrstrassenspezifischen Einstellungen
	 * auszufuehren.
	 */
	protected abstract void einstellenProcess();

	/**
	 * Wird aufgerufen, wenn sich die Besetzung eines Elements (Gleis oder Weiche) geaendert hat.
	 * 
	 * @param dbd Die geaenderte Variable
	 */
	protected abstract void onChangeBesetzung(DBDParameter dbd);
	
	/**
	 * Prueft die Fahrstrassenweichen, Fahrstrassengleise und Fahrstrassensignale auf korrekte Grundstellung. Ist ein
	 * Element nicht in Grundstellung, muss die Fahrstrasse eingespeichert werden, anderenfalls kann sie eingestellt
	 * werden.
	 * 
	 * @return Das Ergebnis der Fahrstrassenpruefung, <code>FAHRSTRASSE_OK</code>, <code>FAHRSTRASSE_SPEICHERN</code>
	 * @see #pruefeWeichen()
	 * @see #pruefeGleise()
	 * @see #pruefeSignale()
	 */
	protected int pruefenProcess() {
		boolean ok = pruefeWeichen()
				  && pruefeGleise()
				  && pruefeSignale();

		return ok ? FAHRSTRASSE_OK : FAHRSTRASSE_SPEICHERN;
	}

	/**
	 * Leere Implementierung, muss in der konkreten Fahrstrasse implementiert werden, um die fahrstrassenspezifischen
	 * Aufloesungen auszufuehren.
	 */
	protected void aufloesenProcess() {
	}
	
	/**
	 * Pruefung der Fahrstrassenweichen auf Lageaenderungen, bei unzulaessiger Aenderung werden die Startsignale der
	 * Fahrstrasse auf Halt gestellt.
	 * 
	 * @see #pruefeLageAenderung(String, String)
	 */
	protected void onChangeWeichenLage(DBDParameter dbd) {
		for (String startSignal : startSignale) {
			pruefeLageAenderung(dbd.getId(), startSignal);
		}
	}
	
	/**
	 * Prueft alle Fahrstrassenweichen, ob sie in die fuer die Fahrstrasse benoetigen Stellung umgestellt werden
	 * koennten.
	 * 
	 * @return <code>true</code> - Die Pruefung der Weichen ist OK
	 * @see #pruefeWeiche(FstWeiche)
	 */
	protected boolean pruefeWeichen() {
		for (FstWeiche fstWeiche : fstWeichen) {
			if (!pruefeWeiche(fstWeiche)) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * Stellt alle Fahrstrassenweichen in die benoetigte Stellung um und gibt einen <code>CountDownLatch</code>
	 * zurueck, der zur Ueberwachung der Endlage aller Weichen dient.
	 * 
	 * @return Der <code>CountDownLatch</code>, der genau dann auf 0 gesetzt wird, wenn alle Fahrstrassenweichen in
	 *         ihre benoetigte Endlage gelaufen sind.
	 * @see Weiche#umstellen(SwitchPosition, CountDownLatch)
	 */
	protected CountDownLatch weichenUmstellen() {
		CountDownLatch latch = new CountDownLatch(fstWeichen.length);
		
		for (FstWeiche fstWeiche : fstWeichen) {
			main.getWeiche(fstWeiche.weichenId).umstellen(fstWeiche.sollLage, latch);
		}
		
		return latch;
	}
	
	/**
	 * Setzt fuer alle Fahrstrassenweichen entweder das <code>Aktiv</code> oder das <code>Flankenschutz</code> Flag
	 * (je nach Definition der Weiche)
	 * 
	 * @see Weiche#setAktiv(boolean)
	 * @see Weiche#setFlankenSchutz(boolean)
	 */
	protected void setzeWeichenAktiv() {
		for (FstWeiche fstWeiche : fstWeichen) {
			if (!fstWeiche.flankenSchutz) {
				main.getWeiche(fstWeiche.weichenId).setAktiv(true);
			} else {
				main.getWeiche(fstWeiche.weichenId).setFlankenSchutz(true);
			}
		}
	}
	
	/**
	 * Loescht fuer alle Fahrstrassenweichen entweder das <code>Aktiv</code> oder das <code>Flankenschutz</code> Flag
	 * (je nach Definition der Weiche)
	 * 
	 * @see Weiche#setAktiv(boolean)
	 * @see Weiche#setFlankenSchutz(boolean)
	 */
	protected void setzeWeichenInaktiv() {
		for (FstWeiche fstWeiche : fstWeichen) {
			if (!fstWeiche.flankenSchutz) {
				main.getWeiche(fstWeiche.weichenId).setAktiv(false);
			} else {
				main.getWeiche(fstWeiche.weichenId).setFlankenSchutz(false);
			}
		}
	}
	
	/**
	 * Prueft, ob die Weichenlageaenderung eine Fahrstrassenweiche betrifft und falls ja, ob die neue Lage der Weiche 
	 * nicht der Lage entspricht, fuer die sie in der Fahrstrasse eingestellt wurde. Ist das der Fall, wird das
	 * Startsignal der Fahrstrasse auf Halt gestellt.
	 * 
	 * @param dbdId Die DBD-ID der Weiche, deren Lage sich geandert hat
	 * @param signalId Die ID des auf Halt zu stellenden Startsignals der Fahrstrasse
	 * @see Signal#setHaltfall()
	 */
	protected void pruefeLageAenderung(String dbdId, String signalId) {
		for (FstWeiche fstWeiche : fstWeichen) {
			if (dbdId.equals(fstWeiche.weichenId) && main.getWeiche(fstWeiche.weichenId).getIstLage() != fstWeiche.sollLage) {
				LOG.warn("Fahrstrasse " + id + ": Weiche " + dbdId + " umgestellt, stelle Signal " + signalId + " auf Halt.");
				main.getSignal(signalId).setHaltfall();
				break;
			}
		}
	}

	/**
	 * Prueft alle Fahrstrassengleise, ob sie fuer die einzustellende Fahrstrasse genutzt werden koennen.
	 * 
	 * @return <code>true</code> - Die Pruefung der Gleise ist OK
	 * @see #pruefeGleis(FstGleis)
	 */
	protected boolean pruefeGleise() {
		for (FstGleis fstGleis : fstGleise) {
			if (!pruefeGleis(fstGleis)) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * Setzt fuer alle Fahrstrassengleise das <code>Aktiv</code> Flag
	 * 
	 * @see Gleis#setAktiv(boolean)
	 */
	protected void setzeGleiseAktiv() {
		for (FstGleis fstGleis : fstGleise) {
			main.getGleis(fstGleis.gleisId).setAktiv(true);
		}
	}
	
	/**
	 * Loescht fuer alle Fahrstrassengleise das <code>Aktiv</code> Flag
	 * 
	 * @see Gleis#setAktiv(boolean)
	 */
	protected void setzeGleiseInaktiv() {
		for (FstGleis fstGleis : fstGleise) {
			main.getGleis(fstGleis.gleisId).setAktiv(false);
		}
	}
	
	/**
	 * Prueft fuer alle Fahrstrassensignle, ob sie in Haltstellung stehen und ggf. der manuelle Haltfall nicht aktiv
	 * ist.
	 * 
	 * @return <code>true</code> - Die Pruefung der Signale ist OK
	 * @see #pruefeSignal(FstSignal)
	 */
	protected boolean pruefeSignale() {
		for (FstSignal fstSignal : fstSignale) {
			if (!pruefeSignal(fstSignal)) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * Setzt alle Startsignale der Fahrstrasse auf Halt
	 * 
	 * @see Signal#setHaltfall()
	 */
	protected void haltfallStartsignale() {
		for (String startSignal : startSignale) {
			main.getSignal(startSignal).setHaltfall();
		}
	}
	
	/**
	 * Prueft die Weiche mit der uebergebenen ID. Die Pruefung ist OK, wenn<br>
	 * <ul>
	 * <li>Bei einer Flankenschutzweiche die Istlage der Weiche mit der Solllage uebereinstimmt <b>oder</b> die Weiche 
	 * nicht aktiv ist, nicht besetzt ist und keinen Flankenschutz bietet</li>
	 * <li>Bei einer Fahrstrassenweiche die Weiche nicht besetzt ist, die Istlage der Weiche mit der Solllage
	 * uebereinstimmg <b>oder</b> die Weiche nicht aktiv ist und keinen Flankenschutz bietet</li>
	 * </ul>
	 * 
	 * @param fstWeiche Die zu pruefende Weiche
	 * @return <code>true</code> - Die Pruefung der Weiche ist OK
	 */
	private boolean pruefeWeiche(FstWeiche fstWeiche) {
		Weiche weiche = main.getWeiche(fstWeiche.weichenId);
		boolean result = false;
		
		if (fstWeiche.flankenSchutz) {
			result = (weiche.getIstLage() == fstWeiche.sollLage) 
				  || (!weiche.isAktiv() && !weiche.isFlankenSchutz() && !weiche.isBesetzt());
		} else {
			if (!weiche.isBesetzt()) {
				result = (weiche.getIstLage() == fstWeiche.sollLage)
					  || (!weiche.isAktiv() && !weiche.isFlankenSchutz());
			}
		}
		
		LOG.debug("Status Weiche: " + weiche.toString());
		LOG.debug("Pruefe Weiche: " + fstWeiche.weichenId + " / " + fstWeiche.sollLage + " / " + fstWeiche.flankenSchutz + " = " + result);
		return result;
	}
	
	/**
	 * Prueft das Gleis mit der uebergebenen ID. Die Pruefung ist OK, wenn<br>
	 * <ul>
	 * <li>das Gleis nicht aktiv ist <b>und</b></li>
	 * <li>das Gleis frei ist <b>oder</b> <code>false</code> im frei Parameter uebergeben wurde</li>
	 * </ul>
	 * 
	 * @param fstGleis Das zu pruefende Gleis
	 * @return <code>true</code> - Die Pruefung des Gleises ist OK
	 */
	private boolean pruefeGleis(FstGleis fstGleis) {
		Gleis gleis = main.getGleis(fstGleis.gleisId);
		boolean result = false;
		
		if (fstGleis.frei) {
			result = !gleis.isAktiv() && !gleis.isBesetzt();
		} else {
			result = !gleis.isAktiv();
		}
		
		LOG.debug("Status Gleis: " + gleis.toString());
		LOG.debug("Pruefe Gleis " + fstGleis.gleisId + " / " + fstGleis.frei + " = " + result);
		return result;
	}
	
	/**
	 * Prueft das Signal mit der uebergebenen ID. Die Pruefung ist OK, wenn<br>
	 * <ul>
	 * <li>das Signal einen Haltbegriff zeigt <b>und</b></li>
	 * <li>der Haltfall-Schalter nicht gesetzt ist <b>oder</b> <code>false</code> im notHaltFallManuell Parameter
	 * uebergeben wurde</li>
	 * </ul>
	 * 
	 * @param fstSignal Das zu pruefende Signal
	 * 
	 * @return <code>true</code> - Die Pruefung des Signals ist OK
	 */
	private boolean pruefeSignal(FstSignal fstSignal) {
		Signal signal = main.getSignal(fstSignal.signalId);
		
		boolean result = signal.isHalt() && (!fstSignal.notHaltfallManuell || !signal.isHaltfallManuell());
		
		LOG.debug("Status Signal: " + signal.toString());
		LOG.debug("Pruefe Signal " + fstSignal.signalId + " / " + fstSignal.notHaltfallManuell + " = " + result);
		return result;
	}
	
	/**
	 * Definition einer Fahrwegweiche mit den folgenden Attributen:<br>
	 * <ul>
	 * <li>ID der Weiche</li>
	 * <li>Soll-Lage der Weiche</li>
	 * <li>Flag, ob die Weiche in der Fahrstrasse eine Flankenschutzweiche ist oder nicht</li>
	 * </ul>
	 */
	protected static class FstWeiche {
		public String weichenId;
		public SwitchPosition sollLage;
		public boolean flankenSchutz;
		
		public FstWeiche(String weichenId, SwitchPosition sollLage, boolean flankenSchutz) {
			this.weichenId = weichenId;
			this.sollLage = sollLage;
			this.flankenSchutz = flankenSchutz;
		}
	}
	
	/**
	 * Definition eines Fahrweggleises mit den folgenden Attributen:<br>
	 * <ul>
	 * <li>ID des Gleises</li>
	 * <li>Flag, ob das Gleis zur Einstellung der Fahrstrasse besetzt sein darf oder nicht</li>
	 * </ul>
	 */
	protected static class FstGleis {
		public String gleisId;
		public boolean frei;

		public FstGleis(String gleisId, boolean frei) {
			this.gleisId = gleisId;
			this.frei = frei;
		}
	}
	
	/**
	 * Definition eines Fahrwegsignals mit den folgenden Attributen:<br>
	 * <ul>
	 * <li>ID des Signals</li>
	 * <li>Flag, ob der manuelle Haltfall fuer das Signal aktiv sein darf oder nicht</li>
	 * </ul>
	 */
	protected static class FstSignal {
		public String signalId;
		public boolean notHaltfallManuell;
		
		public FstSignal(String signalId, boolean notHaltfallManuell) {
			this.signalId = signalId;
			this.notHaltfallManuell = notHaltfallManuell;
		}
	}
}
